/*****************************************************************************
*
*  evnt_handler.c  - CC3000 Host Driver Implementation.
*  Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
* Adapted for use with the Arduino/AVR by KTOWN (Kevin Townsend)
* & Limor Fried for Adafruit Industries
* This library works with the Adafruit CC3000 breakout
*	----> https://www.adafruit.com/products/1469
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*    Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
*    Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the
*    distribution.
*
*    Neither the name of Texas Instruments Incorporated nor the names of
*    its contributors may be used to endorse or promote products derived
*    from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
*  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
*  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
//*****************************************************************************
//
//! \addtogroup evnt_handler_api
//! @{
//
//******************************************************************************

//******************************************************************************
//                  INCLUDE FILES
//******************************************************************************

#include "cc3000_common.h"
#include "string.h"
#include "hci.h"
#include "evnt_handler.h"
#include "wlan.h"
#include "socket.h"
#include "netapp.h"
// Adafruit CC3k Host Driver Difference
// Reference our SPI driver in the parent folder, and include debug header.
// Noted 12-12-2014 by tdicola
#include "../ccspi.h"
#include "debug.h"

//*****************************************************************************
//                  COMMON DEFINES
//*****************************************************************************

#define FLOW_CONTROL_EVENT_HANDLE_OFFSET (0)
#define FLOW_CONTROL_EVENT_BLOCK_MODE_OFFSET (1)
#define FLOW_CONTROL_EVENT_FREE_BUFFS_OFFSET (2)
#define FLOW_CONTROL_EVENT_SIZE (4)

#define BSD_RSP_PARAMS_SOCKET_OFFSET (0)
#define BSD_RSP_PARAMS_STATUS_OFFSET (4)

#define GET_HOST_BY_NAME_RETVAL_OFFSET (0)
#define GET_HOST_BY_NAME_ADDR_OFFSET (4)

#define ACCEPT_SD_OFFSET (0)
#define ACCEPT_RETURN_STATUS_OFFSET (4)
#define ACCEPT_ADDRESS__OFFSET (8)

#define SL_RECEIVE_SD_OFFSET (0)
#define SL_RECEIVE_NUM_BYTES_OFFSET (4)
#define SL_RECEIVE__FLAGS__OFFSET (8)

#define SELECT_STATUS_OFFSET (0)
#define SELECT_READFD_OFFSET (4)
#define SELECT_WRITEFD_OFFSET (8)
#define SELECT_EXFD_OFFSET (12)

#define NETAPP_IPCONFIG_IP_OFFSET (0)
#define NETAPP_IPCONFIG_SUBNET_OFFSET (4)
#define NETAPP_IPCONFIG_GW_OFFSET (8)
#define NETAPP_IPCONFIG_DHCP_OFFSET (12)
#define NETAPP_IPCONFIG_DNS_OFFSET (16)
#define NETAPP_IPCONFIG_MAC_OFFSET (20)
#define NETAPP_IPCONFIG_SSID_OFFSET (26)

#define NETAPP_IPCONFIG_IP_LENGTH (4)
#define NETAPP_IPCONFIG_MAC_LENGTH (6)
#define NETAPP_IPCONFIG_SSID_LENGTH (32)

#define NETAPP_PING_PACKETS_SENT_OFFSET (0)
#define NETAPP_PING_PACKETS_RCVD_OFFSET (4)
#define NETAPP_PING_MIN_RTT_OFFSET (8)
#define NETAPP_PING_MAX_RTT_OFFSET (12)
#define NETAPP_PING_AVG_RTT_OFFSET (16)

#define GET_SCAN_RESULTS_TABlE_COUNT_OFFSET (0)
#define GET_SCAN_RESULTS_SCANRESULT_STATUS_OFFSET (4)
#define GET_SCAN_RESULTS_ISVALID_TO_SSIDLEN_OFFSET (8)
#define GET_SCAN_RESULTS_FRAME_TIME_OFFSET (10)
#define GET_SCAN_RESULTS_SSID_MAC_LENGTH (38)

#define GET_MSS_VAL_RETVAL_OFFSET (0)

//*****************************************************************************
//                  GLOBAL VARAIABLES
//*****************************************************************************

UINT32 socket_active_status = SOCKET_STATUS_INIT_VAL;

#ifdef MDNS_ADVERTISE_HOST
UINT8 localIP[NETAPP_IPCONFIG_IP_LENGTH];
#endif

//*****************************************************************************
//            Prototypes for the static functions
//*****************************************************************************

static INT32 hci_event_unsol_flowcontrol_handler(CHAR *pEvent);

static void update_socket_active_status(CHAR *resp_params);

//*****************************************************************************
//
//!  hci_unsol_handle_patch_request
//!
//!  @param  event_hdr  event header
//!
//!  @return none
//!
//!  @brief   Handle unsolicited event from type patch request
//
//*****************************************************************************
void hci_unsol_handle_patch_request(CHAR *event_hdr) {
  CHAR *params = (CHAR *) (event_hdr) + HCI_EVENT_HEADER_SIZE;
  UINT32 ucLength = 0;
  CHAR *patch;

  switch (*params) {
    case HCI_EVENT_PATCHES_DRV_REQ:

      if (tSLInformation.sDriverPatches) {
        patch = tSLInformation.sDriverPatches(&ucLength);

        if (patch) {
          hci_patch_send(HCI_EVENT_PATCHES_DRV_REQ,
                         tSLInformation.pucTxCommandBuffer, patch, ucLength);
          return;
        }
      }

      // Send 0 length Patches response event
      hci_patch_send(HCI_EVENT_PATCHES_DRV_REQ,
                     tSLInformation.pucTxCommandBuffer, 0, 0);
      break;

    case HCI_EVENT_PATCHES_FW_REQ:

      if (tSLInformation.sFWPatches) {
        patch = tSLInformation.sFWPatches(&ucLength);

        // Build and send a patch
        if (patch) {
          hci_patch_send(HCI_EVENT_PATCHES_FW_REQ,
                         tSLInformation.pucTxCommandBuffer, patch, ucLength);
          return;
        }
      }

      // Send 0 length Patches response event
      hci_patch_send(HCI_EVENT_PATCHES_FW_REQ,
                     tSLInformation.pucTxCommandBuffer, 0, 0);
      break;

    case HCI_EVENT_PATCHES_BOOTLOAD_REQ:

      if (tSLInformation.sBootLoaderPatches) {
        patch = tSLInformation.sBootLoaderPatches(&ucLength);

        if (patch) {
          hci_patch_send(HCI_EVENT_PATCHES_BOOTLOAD_REQ,
                         tSLInformation.pucTxCommandBuffer, patch, ucLength);
          return;
        }
      }

      // Send 0 length Patches response event
      hci_patch_send(HCI_EVENT_PATCHES_BOOTLOAD_REQ,
                     tSLInformation.pucTxCommandBuffer, 0, 0);
      break;
  }
}

//*****************************************************************************
//
//!  hci_event_handler
//!
//!  @param  pRetParams     incoming data buffer
//!  @param  from           from information (in case of data received)
//!  @param  fromlen        from information length (in case of data received)
//!
//!  @return         none
//!
//!  @brief          Parse the incoming events packets and issues corresponding
//!                  event handler from global array of handlers pointers
//
//*****************************************************************************

UINT8 *hci_event_handler(void *pRetParams, UINT8 *from, UINT8 *fromlen) {
  UINT8 *pucReceivedData, ucArgsize;
  UINT16 usLength;
  UINT8 *pucReceivedParams;
  UINT16 usReceivedEventOpcode = 0;
  UINT32 retValue32;
  UINT8 *RecvParams;
  UINT8 *RetParams;

  while (1) {
    // Adafruit CC3k Host Driver Difference
    // Call cc3k_int_poll to try to keep from missing interrupts.
    // Noted 12-12-2014 by tdicola
    cc3k_int_poll();

    if (tSLInformation.usEventOrDataReceived != 0) {
      pucReceivedData = (tSLInformation.pucReceivedData);

      if (*pucReceivedData == HCI_TYPE_EVNT) {
        // Event Received
        STREAM_TO_UINT16((CHAR *) pucReceivedData, HCI_EVENT_OPCODE_OFFSET,
                         usReceivedEventOpcode);
        pucReceivedParams = pucReceivedData + HCI_EVENT_HEADER_SIZE;
        RecvParams = pucReceivedParams;
        // Adafruit CC3k Host Driver Difference
        // Explicit cast of pRetParams to UINT8* to fix compiler warning.
        // Noted 12-12-2014 by tdicola
        RetParams = (UINT8 *) pRetParams;

        // In case unsolicited event received - here the handling finished
        if (hci_unsol_event_handler((CHAR *) pucReceivedData) == 0) {
          STREAM_TO_UINT8(pucReceivedData, HCI_DATA_LENGTH_OFFSET, usLength);

          switch (usReceivedEventOpcode) {
            case HCI_CMND_READ_BUFFER_SIZE: {
              STREAM_TO_UINT8((CHAR *) pucReceivedParams, 0,
                              tSLInformation.usNumberOfFreeBuffers);
              STREAM_TO_UINT16((CHAR *) pucReceivedParams, 1,
                               tSLInformation.usSlBufferLength);
            } break;

            case HCI_CMND_WLAN_CONFIGURE_PATCH:
            case HCI_NETAPP_DHCP:
            case HCI_NETAPP_PING_SEND:
            case HCI_NETAPP_PING_STOP:
            case HCI_NETAPP_ARP_FLUSH:
            case HCI_NETAPP_SET_DEBUG_LEVEL:
            case HCI_NETAPP_SET_TIMERS:
            case HCI_EVNT_NVMEM_READ:
            case HCI_EVNT_NVMEM_CREATE_ENTRY:
            case HCI_CMND_NVMEM_WRITE_PATCH:
            case HCI_NETAPP_PING_REPORT:
            case HCI_EVNT_MDNS_ADVERTISE:

              STREAM_TO_UINT8(pucReceivedData, HCI_EVENT_STATUS_OFFSET,
                              *(UINT8 *) pRetParams);
              break;

            case HCI_CMND_SETSOCKOPT:
            case HCI_CMND_WLAN_CONNECT:
            case HCI_CMND_WLAN_IOCTL_STATUSGET:
            case HCI_EVNT_WLAN_IOCTL_ADD_PROFILE:
            case HCI_CMND_WLAN_IOCTL_DEL_PROFILE:
            case HCI_CMND_WLAN_IOCTL_SET_CONNECTION_POLICY:
            case HCI_CMND_WLAN_IOCTL_SET_SCANPARAM:
            case HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_START:
            case HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_STOP:
            case HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_SET_PREFIX:
            case HCI_CMND_EVENT_MASK:
            case HCI_EVNT_WLAN_DISCONNECT:
            case HCI_EVNT_SOCKET:
            case HCI_EVNT_BIND:
            case HCI_CMND_LISTEN:
            case HCI_EVNT_CLOSE_SOCKET:
            case HCI_EVNT_CONNECT:
            case HCI_EVNT_NVMEM_WRITE:

              STREAM_TO_UINT32((CHAR *) pucReceivedParams, 0,
                               *(UINT32 *) pRetParams);
              break;

            case HCI_EVNT_READ_SP_VERSION:

              STREAM_TO_UINT8(pucReceivedData, HCI_EVENT_STATUS_OFFSET,
                              *(UINT8 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 1;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams, 0, retValue32);
              UINT32_TO_STREAM((UINT8 *) pRetParams, retValue32);
              break;

            case HCI_EVNT_BSD_GETHOSTBYNAME:

              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               GET_HOST_BY_NAME_RETVAL_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               GET_HOST_BY_NAME_ADDR_OFFSET,
                               *(UINT32 *) pRetParams);
              break;

            case HCI_EVNT_GETMSSVALUE:

              STREAM_TO_UINT16((CHAR *) pucReceivedParams,
                               GET_MSS_VAL_RETVAL_OFFSET,
                               *(UINT16 *) pRetParams);

              break;

            case HCI_EVNT_ACCEPT: {
              STREAM_TO_UINT32((CHAR *) pucReceivedParams, ACCEPT_SD_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               ACCEPT_RETURN_STATUS_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;

              // This argument returns in network order
              memcpy((UINT8 *) pRetParams,
                     pucReceivedParams + ACCEPT_ADDRESS__OFFSET,
                     sizeof(sockaddr));
              break;
            }

            case HCI_EVNT_RECV:
            case HCI_EVNT_RECVFROM: {
              STREAM_TO_UINT32((CHAR *) pucReceivedParams, SL_RECEIVE_SD_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               SL_RECEIVE_NUM_BYTES_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               SL_RECEIVE__FLAGS__OFFSET,
                               *(UINT32 *) pRetParams);

              if (((tBsdReadReturnParams *) pRetParams)->iNumberOfBytes ==
                  ERROR_SOCKET_INACTIVE) {
                set_socket_active_status(
                    ((tBsdReadReturnParams *) pRetParams)->iSocketDescriptor,
                    SOCKET_STATUS_INACTIVE);
              }
              break;
            }

            case HCI_EVNT_SEND:
            case HCI_EVNT_SENDTO: {
              STREAM_TO_UINT32((CHAR *) pucReceivedParams, SL_RECEIVE_SD_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               SL_RECEIVE_NUM_BYTES_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;

              break;
            }

            case HCI_EVNT_SELECT: {
              STREAM_TO_UINT32((CHAR *) pucReceivedParams, SELECT_STATUS_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams, SELECT_READFD_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               SELECT_WRITEFD_OFFSET, *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams, SELECT_EXFD_OFFSET,
                               *(UINT32 *) pRetParams);
              break;
            }

            case HCI_CMND_GETSOCKOPT:

              STREAM_TO_UINT8(
                  pucReceivedData, HCI_EVENT_STATUS_OFFSET,
                  ((tBsdGetSockOptReturnParams *) pRetParams)->iStatus);
              // This argument returns in network order
              memcpy((UINT8 *) pRetParams, pucReceivedParams, 4);
              break;

            case HCI_CMND_WLAN_IOCTL_GET_SCAN_RESULTS:

              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               GET_SCAN_RESULTS_TABlE_COUNT_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT32((CHAR *) pucReceivedParams,
                               GET_SCAN_RESULTS_SCANRESULT_STATUS_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 4;
              STREAM_TO_UINT16((CHAR *) pucReceivedParams,
                               GET_SCAN_RESULTS_ISVALID_TO_SSIDLEN_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 2;
              STREAM_TO_UINT16((CHAR *) pucReceivedParams,
                               GET_SCAN_RESULTS_FRAME_TIME_OFFSET,
                               *(UINT32 *) pRetParams);
              pRetParams = ((CHAR *) pRetParams) + 2;
              memcpy((UINT8 *) pRetParams,
                     (CHAR *) (pucReceivedParams +
                               GET_SCAN_RESULTS_FRAME_TIME_OFFSET + 2),
                     GET_SCAN_RESULTS_SSID_MAC_LENGTH);
              break;

            case HCI_CMND_SIMPLE_LINK_START:
              break;

            case HCI_NETAPP_IPCONFIG:

              // Read IP address
              STREAM_TO_STREAM(RecvParams, RetParams,
                               NETAPP_IPCONFIG_IP_LENGTH);
              RecvParams += 4;

              // Read subnet
              STREAM_TO_STREAM(RecvParams, RetParams,
                               NETAPP_IPCONFIG_IP_LENGTH);
              RecvParams += 4;

              // Read default GW
              STREAM_TO_STREAM(RecvParams, RetParams,
                               NETAPP_IPCONFIG_IP_LENGTH);
              RecvParams += 4;

              // Read DHCP server
              STREAM_TO_STREAM(RecvParams, RetParams,
                               NETAPP_IPCONFIG_IP_LENGTH);
              RecvParams += 4;

              // Read DNS server
              STREAM_TO_STREAM(RecvParams, RetParams,
                               NETAPP_IPCONFIG_IP_LENGTH);
              RecvParams += 4;

              // Read Mac address
              STREAM_TO_STREAM(RecvParams, RetParams,
                               NETAPP_IPCONFIG_MAC_LENGTH);
              RecvParams += 6;

              // Read SSID
              STREAM_TO_STREAM(RecvParams, RetParams,
                               NETAPP_IPCONFIG_SSID_LENGTH);
          }
        }

        if (usReceivedEventOpcode == tSLInformation.usRxEventOpcode) {
          tSLInformation.usRxEventOpcode = 0;
        }
      } else {
        pucReceivedParams = pucReceivedData;
        STREAM_TO_UINT8((CHAR *) pucReceivedData, HCI_PACKET_ARGSIZE_OFFSET,
                        ucArgsize);

        STREAM_TO_UINT16((CHAR *) pucReceivedData, HCI_PACKET_LENGTH_OFFSET,
                         usLength);

        // Data received: note that the only case where from and from length
        // are not null is in recv from, so fill the args accordingly
        if (from) {
          STREAM_TO_UINT32((CHAR *) (pucReceivedData + HCI_DATA_HEADER_SIZE),
                           BSD_RECV_FROM_FROMLEN_OFFSET, *(UINT32 *) fromlen);
          memcpy(from, (pucReceivedData + HCI_DATA_HEADER_SIZE +
                        BSD_RECV_FROM_FROM_OFFSET),
                 *fromlen);
        }

        memcpy(pRetParams, pucReceivedParams + HCI_DATA_HEADER_SIZE + ucArgsize,
               usLength - ucArgsize);

        tSLInformation.usRxDataPending = 0;
      }

      tSLInformation.usEventOrDataReceived = 0;

      SpiResumeSpi();

      // Since we are going to TX - we need to handle this event after the
      // ResumeSPi since we need interrupts
      if ((*pucReceivedData == HCI_TYPE_EVNT) &&
          (usReceivedEventOpcode == HCI_EVNT_PATCHES_REQ)) {
        hci_unsol_handle_patch_request((CHAR *) pucReceivedData);
      }

      if ((tSLInformation.usRxEventOpcode == 0) &&
          (tSLInformation.usRxDataPending == 0)) {
        return NULL;
      }
    }
  }
}

//*****************************************************************************
//
//!  hci_unsol_event_handler
//!
//!  @param  event_hdr   event header
//!
//!  @return             1 if event supported and handled
//!                      0 if event is not supported
//!
//!  @brief              Handle unsolicited events
//
//*****************************************************************************
INT32 hci_unsol_event_handler(CHAR *event_hdr) {
  CHAR *data = NULL;
  INT32 event_type;
  UINT32 NumberOfReleasedPackets;
  UINT32 NumberOfSentPackets;

  STREAM_TO_UINT16(event_hdr, HCI_EVENT_OPCODE_OFFSET, event_type);

  // Adafruit CC3k Host Driver Difference
  // Extra debug output.
  // Noted 12-12-2014 by tdicola
  DEBUGPRINT_F("\tHCI_UNSOL_EVT: ");
  DEBUGPRINT_HEX16(event_type);

  if (event_type & HCI_EVNT_UNSOL_BASE) {
    switch (event_type) {
      case HCI_EVNT_DATA_UNSOL_FREE_BUFF: {
        hci_event_unsol_flowcontrol_handler(event_hdr);

        NumberOfReleasedPackets = tSLInformation.NumberOfReleasedPackets;
        NumberOfSentPackets = tSLInformation.NumberOfSentPackets;

        if (NumberOfReleasedPackets == NumberOfSentPackets) {
          if (tSLInformation.InformHostOnTxComplete) {
            tSLInformation.sWlanCB(HCI_EVENT_CC3000_CAN_SHUT_DOWN, NULL, 0);
          }
        }
        return 1;
      }
    }
  }

  if (event_type & HCI_EVNT_WLAN_UNSOL_BASE) {
    switch (event_type) {
      case HCI_EVNT_WLAN_KEEPALIVE:
      case HCI_EVNT_WLAN_UNSOL_CONNECT:
      case HCI_EVNT_WLAN_UNSOL_DISCONNECT:
      case HCI_EVNT_WLAN_UNSOL_INIT:
      case HCI_EVNT_WLAN_ASYNC_SIMPLE_CONFIG_DONE:

        if (tSLInformation.sWlanCB) {
          tSLInformation.sWlanCB(event_type, 0, 0);
        }
        break;

      case HCI_EVNT_WLAN_UNSOL_DHCP: {
        UINT8 params[NETAPP_IPCONFIG_MAC_OFFSET +
                     1];  // extra byte is for the status
        UINT8 *recParams = params;

        data = (CHAR *) (event_hdr) + HCI_EVENT_HEADER_SIZE;

        // Read IP address
        STREAM_TO_STREAM(data, recParams, NETAPP_IPCONFIG_IP_LENGTH);
        data += 4;

#ifdef MDNS_ADVERTISE_HOST
        localIP[0] = *(recParams - NETAPP_IPCONFIG_IP_LENGTH);
        localIP[1] = *(recParams - NETAPP_IPCONFIG_IP_LENGTH + 1);
        localIP[2] = *(recParams - NETAPP_IPCONFIG_IP_LENGTH + 2);
        localIP[3] = *(recParams - NETAPP_IPCONFIG_IP_LENGTH + 3);
#endif
        // Read subnet
        STREAM_TO_STREAM(data, recParams, NETAPP_IPCONFIG_IP_LENGTH);
        data += 4;
        // Read default GW
        STREAM_TO_STREAM(data, recParams, NETAPP_IPCONFIG_IP_LENGTH);
        data += 4;
        // Read DHCP server
        STREAM_TO_STREAM(data, recParams, NETAPP_IPCONFIG_IP_LENGTH);
        data += 4;
        // Read DNS server
        STREAM_TO_STREAM(data, recParams, NETAPP_IPCONFIG_IP_LENGTH);
        // read the status
        STREAM_TO_UINT8(event_hdr, HCI_EVENT_STATUS_OFFSET, *recParams);

        if (tSLInformation.sWlanCB) {
          tSLInformation.sWlanCB(event_type, (CHAR *) params, sizeof(params));
        }
      } break;

      case HCI_EVNT_WLAN_ASYNC_PING_REPORT: {
        netapp_pingreport_args_t params;
        data = (CHAR *) (event_hdr) + HCI_EVENT_HEADER_SIZE;
        STREAM_TO_UINT32(data, NETAPP_PING_PACKETS_SENT_OFFSET,
                         params.packets_sent);
        STREAM_TO_UINT32(data, NETAPP_PING_PACKETS_RCVD_OFFSET,
                         params.packets_received);
        STREAM_TO_UINT32(data, NETAPP_PING_MIN_RTT_OFFSET,
                         params.min_round_time);
        STREAM_TO_UINT32(data, NETAPP_PING_MAX_RTT_OFFSET,
                         params.max_round_time);
        STREAM_TO_UINT32(data, NETAPP_PING_AVG_RTT_OFFSET,
                         params.avg_round_time);

        if (tSLInformation.sWlanCB) {
          tSLInformation.sWlanCB(event_type, (CHAR *) &params, sizeof(params));
        }
      } break;
      case HCI_EVNT_BSD_TCP_CLOSE_WAIT: {
        // Adafruit CC3k Host Driver Difference
        // Extra debug output.
        // Noted 12-12-2014 by tdicola
        DEBUGPRINT_F("\tTCP Close Wait\n\r");
        data = (CHAR *) (event_hdr) + HCI_EVENT_HEADER_SIZE;
        if (tSLInformation.sWlanCB) {
          // data[0] represents the socket id, for which FIN was received by
          // remote.
          // Upon receiving this event, the user can close the socket, or else
          // the
          // socket will be closded after inacvitity timeout (by default 60
          // seconds)
          tSLInformation.sWlanCB(event_type, data, 1);
        }
      } break;

      case HCI_EVNT_ASYNC_ARP_DONE:
      case HCI_EVNT_ASYNC_ARP_WAITING:
        if (tSLInformation.sWlanCB) {
          tSLInformation.sWlanCB(event_type, 0, 0);
        }
        break;

      //'default' case which means "event not supported"
      default:
        return (0);
    }
    return (1);
  }

  if ((event_type == HCI_EVNT_SEND) || (event_type == HCI_EVNT_SENDTO) ||
      (event_type == HCI_EVNT_WRITE)) {
    CHAR *pArg;
    INT32 status;

    // Adafruit CC3k Host Driver Difference
    // Extra debug output.
    // Noted 12-12-2014 by tdicola
    DEBUGPRINT_F("\tSEND event response\n\r");

    pArg = M_BSD_RESP_PARAMS_OFFSET(event_hdr);
    STREAM_TO_UINT32(pArg, BSD_RSP_PARAMS_STATUS_OFFSET, status);

    if (ERROR_SOCKET_INACTIVE == status) {
      // The only synchronous event that can come from SL device in form of
      // command complete is "Command Complete" on data sent, in case SL device
      // was unable to transmit
      STREAM_TO_UINT8(event_hdr, HCI_EVENT_STATUS_OFFSET,
                      tSLInformation.slTransmitDataError);
      update_socket_active_status(M_BSD_RESP_PARAMS_OFFSET(event_hdr));

      return (1);
    } else
      return (0);
  }

  // handle a case where unsolicited event arrived, but was not handled by any
  // of the cases above
  if ((event_type != tSLInformation.usRxEventOpcode) &&
      (event_type != HCI_EVNT_PATCHES_REQ)) {
    return (1);
  }

  return (0);
}

//*****************************************************************************
//
//!  hci_unsolicited_event_handler
//!
//!  @param None
//!
//!  @return         ESUCCESS if successful, EFAIL if an error occurred
//!
//!  @brief          Parse the incoming unsolicited event packets and issues
//!                  corresponding event handler.
//
//*****************************************************************************
INT32 hci_unsolicited_event_handler(void) {
  UINT32 res = 0;
  UINT8 *pucReceivedData;

  if (tSLInformation.usEventOrDataReceived != 0) {
    pucReceivedData = (tSLInformation.pucReceivedData);

    if (*pucReceivedData == HCI_TYPE_EVNT) {
      // In case unsolicited event received - here the handling finished
      if (hci_unsol_event_handler((CHAR *) pucReceivedData) == 1) {
        // There was an unsolicited event received - we can release the buffer
        // and clean the event received
        tSLInformation.usEventOrDataReceived = 0;

        res = 1;
        SpiResumeSpi();
      }
    }
  }

  return res;
}

//*****************************************************************************
//
//!  set_socket_active_status
//!
//!  @param Sd
//!	 @param Status
//!  @return         none
//!
//!  @brief          Check if the socket ID and status are valid and set
//!                  accordingly  the global socket status
//
//*****************************************************************************
void set_socket_active_status(INT32 Sd, INT32 Status) {
  if (M_IS_VALID_SD(Sd) && M_IS_VALID_STATUS(Status)) {
    socket_active_status &= ~(1 << Sd);     /* clean socket's mask */
    socket_active_status |= (Status << Sd); /* set new socket's mask */
  }
}

//*****************************************************************************
//
//!  hci_event_unsol_flowcontrol_handler
//!
//!  @param  pEvent  pointer to the string contains parameters for IPERF
//!  @return         ESUCCESS if successful, EFAIL if an error occurred
//!
//!  @brief  Called in case unsolicited event from type
//!          HCI_EVNT_DATA_UNSOL_FREE_BUFF has received.
//!				   Keep track on the number of packets
//transmitted
// and update the
//!					 number of free buffer in the SL device.
//
//*****************************************************************************
INT32 hci_event_unsol_flowcontrol_handler(CHAR *pEvent) {
  INT32 temp, value;
  UINT16 i;
  UINT16 pusNumberOfHandles = 0;
  CHAR *pReadPayload;

  STREAM_TO_UINT16((CHAR *) pEvent, HCI_EVENT_HEADER_SIZE, pusNumberOfHandles);
  pReadPayload =
      ((CHAR *) pEvent + HCI_EVENT_HEADER_SIZE + sizeof(pusNumberOfHandles));
  temp = 0;

  for (i = 0; i < pusNumberOfHandles; i++) {
    STREAM_TO_UINT16(pReadPayload, FLOW_CONTROL_EVENT_FREE_BUFFS_OFFSET, value);
    temp += value;
    pReadPayload += FLOW_CONTROL_EVENT_SIZE;
  }

  tSLInformation.usNumberOfFreeBuffers += temp;
  tSLInformation.NumberOfReleasedPackets += temp;

  return (ESUCCESS);
}

//*****************************************************************************
//
//!  get_socket_active_status
//!
//!  @param  Sd  Socket IS
//!  @return     Current status of the socket.
//!
//!  @brief  Retrieve socket status
//
//*****************************************************************************

INT32 get_socket_active_status(INT32 Sd) {
  if (M_IS_VALID_SD(Sd)) {
    return (socket_active_status & (1 << Sd)) ? SOCKET_STATUS_INACTIVE
                                              : SOCKET_STATUS_ACTIVE;
  }
  return SOCKET_STATUS_INACTIVE;
}

//*****************************************************************************
//
//!  update_socket_active_status
//!
//!  @param  resp_params  Socket IS
//!  @return     Current status of the socket.
//!
//!  @brief  Retrieve socket status
//
//*****************************************************************************
void update_socket_active_status(CHAR *resp_params) {
  INT32 status, sd;

  STREAM_TO_UINT32(resp_params, BSD_RSP_PARAMS_SOCKET_OFFSET, sd);
  STREAM_TO_UINT32(resp_params, BSD_RSP_PARAMS_STATUS_OFFSET, status);

  if (ERROR_SOCKET_INACTIVE == status) {
    set_socket_active_status(sd, SOCKET_STATUS_INACTIVE);
  }
}

//*****************************************************************************
//
//!  SimpleLinkWaitEvent
//!
//!  @param  usOpcode      command operation code
//!  @param  pRetParams    command return parameters
//!
//!  @return               none
//!
//!  @brief                Wait for event, pass it to the hci_event_handler and
//!                        update the event opcode in a global variable.
//
//*****************************************************************************

void SimpleLinkWaitEvent(UINT16 usOpcode, void *pRetParams) {
  // In the blocking implementation the control to caller will be returned only
  // after the end of current transaction
  tSLInformation.usRxEventOpcode = usOpcode;
  hci_event_handler(pRetParams, 0, 0);
}

//*****************************************************************************
//
//!  SimpleLinkWaitData
//!
//!  @param  pBuf       data buffer
//!  @param  from       from information
//!  @param  fromlen	from information length
//!
//!  @return               none
//!
//!  @brief                Wait for data, pass it to the hci_event_handler
//! 					   and update in a global variable that
//there
// is
//!						   data to read.
//
//*****************************************************************************

void SimpleLinkWaitData(UINT8 *pBuf, UINT8 *from, UINT8 *fromlen) {
  // In the blocking implementation the control to caller will be returned only
  // after the end of current transaction, i.e. only after data will be received
  tSLInformation.usRxDataPending = 1;
  hci_event_handler(pBuf, from, fromlen);
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
